package com.mokairui.share_model_nolock;

import lombok.extern.slf4j.Slf4j;
import sun.misc.Unsafe;

/**
 * @Description 使用 Unsafe 自定义一个原子类
 * @Author Mokairui
 * @Since 2021/10/6
 */
@Slf4j(topic = "c.atomicData")
public class AtomicData {
    private volatile int data;
    static final Unsafe UNSAFE;
    static final long DATA_OFFSET;

    static {
        try {
            UNSAFE = UnsafeAccessor.getUnsafe();
            // data 属性在 DataContainer 对象中的偏移量, 用于 Unsafe 直接访问该属性
            DATA_OFFSET = UNSAFE.objectFieldOffset(AtomicData.class.getDeclaredField("data"));
        } catch (NoSuchFieldException e) {
            throw new Error(e);
        }
    }

    public AtomicData(int data) {
        this.data = data;
    }

    public void decrease(int amount) {
        int oldValue;
        while (true) {
            // 获取共享变量旧值, 可以在这一行加入断点, 修改data来调式
            oldValue = data;
            // cas 尝试修改data为 旧值 + amount, 如果期间旧值被别的线程修改了, 返回false
            if (UNSAFE.compareAndSwapInt(this, DATA_OFFSET, oldValue, oldValue - amount)) {
                // 修改成功, 跳出
                break;
            }
        }
    }

    public int getData() {
        return data;
    }

    public static void main(String[] args) {
        Account.demo(new Account() {
            AtomicData atomicData = new AtomicData(10000);
            @Override
            public Integer getBalance() {
                return atomicData.getData();
            }

            @Override
            public void withdraw(Integer amount) {
                atomicData.decrease(amount);
            }
        });
    }
}
